﻿using System;
using System.Threading;
using System.Windows.Forms;

namespace DCS.Extensions.WindowsForms
{
    public static class InvokeSyncExtender
    {
        private const bool UseWaitForCreating = false;
        private const int CreatingIteratingTimeout = 100;

        private static void WaitForControlCreation(Control c)
        {
            while (false == c.IsHandleCreated && UseWaitForCreating)
            {
                Thread.Sleep(CreatingIteratingTimeout);
            }
        }

        public static TRet InvokeSync<TRet>(this Control c, Func<TRet> func)
        {
            WaitForControlCreation(c);


            if (c.InvokeRequired)
            {
                return (TRet)c.Invoke(func);
            }
            else
            {
                return func();
            }
        }
        public static TRet InvokeSync<T, TRet>(this Control c, Func<T, TRet> func, T argument)
        {
            WaitForControlCreation(c);

            if (c.InvokeRequired)
            {
                return (TRet)c.Invoke(func, argument);
            }
            else
            {
                return func(argument);
            }
        }
        public static TRet InvokeSync<T1, T2, TRet>(this Control c, Func<T1, T2, TRet> func, T1 argument1, T2 argument2)
        {
            WaitForControlCreation(c);
            
            if (c.InvokeRequired)
                return (TRet)c.Invoke(func, argument1, argument2);
            else
            {    
                return func(argument1, argument2);
            }
        }
        public static TRet InvokeSync<T1, T2, T3, TRet>(this Control c, Func<T1, T2, T3, TRet> func, T1 argument1, T2 argument2,
                                                        T3 argument3)
        {
            WaitForControlCreation(c);

            if (c.InvokeRequired)
                return (TRet)c.Invoke(func, argument1, argument2, argument3);
            else
            return func(argument1, argument2, argument3);
        }
        public static TRet InvokeSync<T1, T2, T3, T4, TRet>(this Control c, Func<T1, T2, T3, T4, TRet> func, T1 argument1, T2 argument2,
                                                            T3 argument3, T4 argument4)
        {
            WaitForControlCreation(c);

            if (c.InvokeRequired)
                return (TRet)c.Invoke(func, argument1, argument2, argument3, argument4);
            else
            {    
                return func(argument1, argument2, argument3, argument4);
            }
        }

        public static void InvokeSync(this Control c, Action func)
        {
            WaitForControlCreation(c);
            
            if (c.IsDisposed)
                return;
            
            if (c.InvokeRequired)
                c.Invoke(func);
            else
                func();
        }

        public static void InvokeSync<T>(this Control c, Action<T> func, T argument)
        {
            WaitForControlCreation(c);

            if (c.IsDisposed)
                return;

            if (c.InvokeRequired)
                c.Invoke(func, argument);
            else
            {
                func(argument);
            }
        }
        public static void InvokeSync<T1, T2>(this Control c, Action<T1, T2> func, T1 argument1, T2 argument2)
        {
            WaitForControlCreation(c);

            if (c.IsDisposed)
                return;

            if (c.InvokeRequired)
                c.Invoke(func, argument1, argument2);
            else
            {
                func(argument1, argument2);
            }
        }
        public static void InvokeSync<T1, T2, T3>(this Control c, Action<T1, T2, T3> func, T1 argument1, T2 argument2,
                                                  T3 argument3)
        {
            WaitForControlCreation(c);

            if (c.IsDisposed)
                return;

            if (c.InvokeRequired)
                c.Invoke(func, argument1, argument2, argument3);
            else
            {
                func(argument1, argument2, argument3);
            }
        }

        public static void InvokeSync<T1, T2, T3, T4>(this Control c, Action<T1, T2, T3, T4> func, T1 argument1, T2 argument2,
                                                      T3 argument3, T4 argument4)
        {
            WaitForControlCreation(c);

            if (c.IsDisposed)
                return;

            if (c.InvokeRequired)
                c.Invoke(func, argument1, argument2, argument3, argument4);
            else
            {    
                func(argument1, argument2, argument3, argument4);
            }
        }
    }
}